﻿using UnityEngine;
using System.Collections;

public class EnemyPhysics : MonoBehaviour {

	public LayerMask hardCollisionMask;
	public LayerMask softCollisionMask;

	private BoxCollider collider;

	private int collisionDivisionsX = 3;
	private int collisionDivisionsY =10;

	private float skin = 0.005f;



	[HideInInspector] public bool grounded;
	[HideInInspector] private bool movementStopped;

	// Information Variables
	[HideInInspector] public bool isAtBorder;
	[HideInInspector] public bool canFallDown;
	[HideInInspector] public float ledgeHeight;
	[HideInInspector] public bool canClimb;

	private bool dropDown = false;

	// Use this for initialization
	void Start () {
		collider = GetComponent<BoxCollider>();
	}

	public void Move(Vector2 moveAmount) {

		Ray ray;
		RaycastHit hit;

		float deltaY = moveAmount.y;
		float deltaX = moveAmount.x;
		float dirY = Mathf.Sign(deltaY);
		float dirX = deltaX == 0 ? 0 : Mathf.Sign(deltaX);
		Vector2 p = transform.position;

		LayerMask verticalLayerToCheck = hardCollisionMask;
		if (dirY < 0) {
			verticalLayerToCheck |= softCollisionMask;
		}
	
		#region Vertical Check
		grounded = false;
		isAtBorder = false;
		for (int i = 0; i < collisionDivisionsX; i++) {

			float x = (p.x + collider.center.x - collider.size.x / 2)+ collider.size.x / (collisionDivisionsX -1) * i;
			float y = p.y + collider.center.y + collider.size.y / 2 * dirY;

			ray = new Ray(new Vector2(x, y), Vector3.down);
			Debug.DrawRay(ray.origin, ray.direction, Color.blue);

			if (Physics.Raycast(ray, out hit, Mathf.Abs(deltaY) + (skin * 2), verticalLayerToCheck)) {

				float dst = Vector3.Distance(ray.origin, hit.point);

				if (dst > skin) {
					deltaY = dst * dirY - skin * dirY;
				} else {
					deltaY = 0;
				}
				grounded = true;
			} else {
				if (dirX < 0 && i == 0) {
					isAtBorder = true;

					// Check height of the ledge
					ray = new Ray(new Vector3(p.x + collider.center.x - collider.size.x / 2, p.y + collider.center.y - collider.size.y / 2), Vector3.down);
					if (Physics.Raycast(ray, out hit, float.MaxValue, verticalLayerToCheck)) {
						ledgeHeight = hit.distance;
					} else {
						ledgeHeight = float.MaxValue;
					}
				}

				if (dirX > 0 && i == collisionDivisionsX -1) {
					isAtBorder = true;

					// Check height of the ledge
					ray = new Ray(new Vector3(p.x + collider.center.x - collider.size.x / 2, p.y + collider.center.y + collider.size.y / 2), Vector3.down);
					if (Physics.Raycast(ray, out hit, float.MaxValue, verticalLayerToCheck)) {
						ledgeHeight = hit.distance;
					} else {
						ledgeHeight = float.MaxValue;
					}
				}
			}
		}

		if (!grounded) {
			dropDown = false;
		}
		#endregion

		if (isAtBorder && grounded && !dropDown) {
			deltaX = 0;
		}

		#region Horizontal Check
		movementStopped = false;
		canClimb = false;
		for (int i = 0; i < collisionDivisionsY; i++) {
			float dir = Mathf.Sign(deltaX);
			float x = p.x + collider.center.x + collider.size.x / 2 * dir;
			float y = p.y + collider.center.y - collider.size.y / 2 + collider.size.y / (collisionDivisionsY - 1) * i;

			ray = new Ray(new Vector2(x, y), new Vector2(dir, 0));
			Debug.DrawRay(ray.origin, ray.direction, Color.blue);

			if (Physics.Raycast(ray, out hit, Mathf.Abs(deltaX) + skin, hardCollisionMask)) {
				float dst = Vector3.Distance(ray.origin, hit.point);

				if (dst > skin) {
					deltaX = dst * dir - skin * dir;
				} else {
					deltaX = 0;
				}
				movementStopped = true;
				break;
			}
		}
		#endregion

		if (!grounded && !movementStopped) {
			Vector3 enemyDir = new Vector3(deltaX, deltaY);
			Vector3 o = new Vector3(p.x + collider.center.x + collider.size.x/2 * Mathf.Sign(deltaX),p.y + collider.center.y + collider.size.y/2 * Mathf.Sign(deltaY));
			ray = new Ray(o, enemyDir.normalized);

			Debug.DrawRay(ray.origin, ray.direction, Color.cyan);

			if (Physics.Raycast(ray,Mathf.Sqrt(deltaX * deltaX + deltaY * deltaY), hardCollisionMask)) {
				grounded = true;
				deltaY = 0;
			}
		}

		Vector2 finalTransform = new Vector2(deltaX, deltaY);
		transform.Translate(finalTransform, Space.World);
	}

	public void DropDown() {
		dropDown = true;
	}
}
